package com.xzone.material.security.config

import com.xzone.material.security.MyAuthenticationEntryPoint
import com.xzone.material.security.authentication.*
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.HttpMethod
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.authentication.AuthenticationProvider
import org.springframework.security.authentication.ProviderManager
import org.springframework.security.authentication.dao.DaoAuthenticationProvider
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configurers.CorsConfigurer
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer
import org.springframework.security.config.http.SessionCreationPolicy
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.security.web.SecurityFilterChain
import org.springframework.security.web.access.AccessDeniedHandler
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter

@Configuration
@EnableWebSecurity
class MyWebSecurityConfig {
    val LOGIN_PROCESSING_URL = "/doLogin"
    val LOGOUT_PROCESSING_URL = "/doLogout"

    @Bean
    fun passwordEncoder(): PasswordEncoder {
        return BCryptPasswordEncoder()
    }

    @Bean
    fun authenticationProvider(
        passwordEncoder: PasswordEncoder,
        userDetailsService: UserDetailsService,
    ): AuthenticationProvider {
        val provider = DaoAuthenticationProvider()
        provider.setPasswordEncoder(passwordEncoder)
        provider.setUserDetailsService(userDetailsService)
        return provider
    }

    @Bean
    fun authenticationManager(authenticationProvider: AuthenticationProvider): AuthenticationManager {
        return ProviderManager(authenticationProvider)
    }

    @Bean
    fun jsonUsernamePasswordAuthenticationFilter(
        authenticationManager: AuthenticationManager,
        myAuthenticationSuccessHandler: MyAuthenticationSuccessHandler,
        myAuthenticationFailureHandler: MyAuthenticationFailureHandler,
    ): JsonUsernamePasswordAuthenticationFilter {
        val jsonUsernamePasswordAuthenticationFilter = JsonUsernamePasswordAuthenticationFilter()
        jsonUsernamePasswordAuthenticationFilter.setAuthenticationManager(authenticationManager)
        jsonUsernamePasswordAuthenticationFilter.setFilterProcessesUrl(LOGIN_PROCESSING_URL)
        jsonUsernamePasswordAuthenticationFilter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler)
        jsonUsernamePasswordAuthenticationFilter.setAuthenticationFailureHandler(myAuthenticationFailureHandler)
        return jsonUsernamePasswordAuthenticationFilter
    }

    @Bean
    fun securityFilterChain(
        httpSecurity: HttpSecurity,
        authenticationProvider: AuthenticationProvider,
        myAuthenticationEntryPoint: MyAuthenticationEntryPoint,
        myAccessDeniedHandler: AccessDeniedHandler,
        myAuthenticationSuccessHandler: MyAuthenticationSuccessHandler,
        myAuthenticationFailureHandler: MyAuthenticationFailureHandler,
        myLogoutSuccessHandler: MyLogoutSuccessHandler,
        jwtSecurityFilter: JwtSecurityFilter,
        jsonUsernamePasswordAuthenticationFilter: JsonUsernamePasswordAuthenticationFilter,
    ): SecurityFilterChain {
        httpSecurity
            .csrf(CsrfConfigurer<HttpSecurity>::disable)
            .cors(CorsConfigurer<HttpSecurity>::disable)
            .httpBasic { httpBasic -> httpBasic.disable() }
            .authenticationProvider(authenticationProvider)
//            .formLogin { configurer -> configurer.disable() }//禁用默认登录页面
            .formLogin { configurer ->
                configurer.loginProcessingUrl(LOGIN_PROCESSING_URL)
                configurer.successHandler(myAuthenticationSuccessHandler)
                configurer.failureHandler(myAuthenticationFailureHandler)
            }
//            .logout { configurer -> configurer.disable() } //禁用默认登出页面
            .logout { configurer ->
                configurer.logoutUrl(LOGOUT_PROCESSING_URL)
                configurer.logoutSuccessHandler(myLogoutSuccessHandler)
            }
            .sessionManagement { session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS) }//禁用session，使用jwt
            .addFilterBefore(jwtSecurityFilter, UsernamePasswordAuthenticationFilter::class.java)
            .addFilterBefore(jsonUsernamePasswordAuthenticationFilter, UsernamePasswordAuthenticationFilter::class.java)
            .authorizeHttpRequests { request ->
                request
                    // TODO 白名单
//                    .requestMatchers(HttpMethod.POST, "/security/login").permitAll()
                    .requestMatchers(HttpMethod.GET, "/user/hello").permitAll()
                    // TODO 接口权限
                    .requestMatchers(HttpMethod.GET, "/user/info").hasAnyAuthority("info")
                    // 判断角色
//                    .requestMatchers(HttpMethod.GET, "/user/manage").hasAnyRole("admin")
                    // 要求登录
                    .anyRequest().authenticated()
            }
            .exceptionHandling { exception ->
                exception.authenticationEntryPoint(myAuthenticationEntryPoint) // 未登录的处理器
                exception.accessDeniedHandler(myAccessDeniedHandler) // 未授权
            }
        return httpSecurity.build()
    }
}